
var ran = Math.random;
var num_poly = 50;
var num_corners = 3;
var num_particles = 40;
var weight = [0.6, 0.8];
var mutations = 0;
var mutation_chance = 0.5;

var space_vel = 1;
var color_vel = 1;

var particles = [];

var gbest = [];
var gbest_val = null;
var gbest_index;

//dimensions description:
//for each polygon, the first 4 dim represent colors, the next ones represent xy coords of the vertices

function particle(w,h)
{
    this.pos = [];
    this.vel = [];
    this.pbest = [];
    this.pbest_val = null;
    
    var poly_length = 4 + num_corners*2;
    var d;
    for (d = 0; d < poly_length*num_poly; d += poly_length)
    {
        this.pos[d] = 255*ran();
        this.pos[d + 1] = 255*ran();
        this.pos[d + 2] = 255*ran();
        this.pos[d + 3] = 255*ran();
        
        this.vel[d] = color_vel*(ran() - 2);
        this.vel[d + 1] = color_vel*(ran() - 2);
        this.vel[d + 2] = color_vel*(ran() - 2);
        this.vel[d + 3] = color_vel*(ran() - 2);
        
        for (var j=0; j < num_corners; j++)
        {
            this.pos[d + 4 + j*2] = ran()*w;
            this.pos[d + 5 + j*2] = ran()*h;
            
            this.vel[d + 4 + j*2] = space_vel*(ran()-2);
            this.vel[d + 5 + j*2] = space_vel*(ran()-2);
        }
    }
    this.pbest = this.pos.slice();
}

particle.prototype.draw = function(ctx)
{
    ctx.fillStyle = 'black';
    ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height);
    
    var poly_length = 4 + num_corners * 2;
    for (var i = 0; i < poly_length*num_poly; i += poly_length)
    {
        ctx.fillStyle = "rgba(" + (this.pos[i]>>0) + "," + (this.pos[i + 1]>>0) + "," + (this.pos[i + 2]>>0) + "," + this.pos[i+3]/255 + ")";
        
        //alert(ctx.fillStyle);
        ctx.beginPath();
        ctx.moveTo(this.pos[i+4],this.pos[i+5]);
        for (var c = 0; c < num_corners - 1; c++)
        {
            ctx.lineTo(this.pos[i + 6 + c*2], this.pos[i + 7 + c*2]);
        }
        ctx.fill();
    }
}

particle.prototype.peval = function(ctx, data)
{
    with(this)
    {
        draw(ctx);
        var pdata;
        try 
        {
            pdata = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;
        }
        catch(e){
            try
            {
                netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
                pdata = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;
            }
            catch(ev){}
        }
        var retval = 0;
        
        var pix = ctx.canvas.width * ctx.canvas.height * 4;
        while(--pix)
        {
            if (pix%3)//only compare rgb
            {
                var diff = pdata[pix]-data[pix];
                retval += diff*diff;
            }
        }
        return (1 - retval / (ctx.canvas.width * ctx.canvas.height * 3 * 256 * 256));
    }
}

function init_pso(ctx)
{
    for (var i = 0; i < num_particles; i++)
    {
        particles[i] = new particle(ctx.canvas.width, ctx.canvas.height);
    }
    gbest = particles[0].pbest.slice();
}

function getWeight()
{
    return (Math.sin(iteration/2/Math.PI) + 1)*(weight[1] - weight[0])/2 + weight[0];
}

function update_pso(ctx)
{
    //mutate
    if (ran() < mutation_chance)
    {
        particles[(ran()*particles.length)>>0] = new particle(ctx.canvas.width, ctx.canvas.height);
        mutations++;
    }
    //main pso
    for (var i=0; i < particles.length; i++)
    {
        with(particles[i])
        {
            for (var d=0; d < pos.length; d++)
            {
            vel[d] = getWeight()*vel[d] +
                     ran()*(pbest[d] - pos[d]) +
                     ran()*(gbest[d] - pos[d]);
            }
        }
    }
    //pos = pos + vel;
    var poly_length = 4 + num_corners * 2;
    for (var i=0; i < particles.length; i++)
    {
        with(particles[i])
        {
            for (var d=0; d < pos.length; d++)
            {
                pos[d] = pos[d] + vel[d];
                //TODO: verify boundaries
                if (d % poly_length < 4)
                {
                    if (pos[d] < 0)
                    {
                        pos[d] = 0;
                        //vel[d] = -vel[d]/2;
                    }
                    if (pos[d] > 255)
                    {
                        pos[d] = 255;
                        //vel[d] = -vel[d]/2;
                    }
                }
                else
                {
                    if (pos[d] < -10)
                    {
                        pos[d] = -10;
                        vel[d] = -vel[d]/2;
                    }
                    if (pos[d] > ctx.canvas.width + 10)
                    {
                        pos[d] = ctx.canvas.width + 10;
                        vel[d] = -vel[d]/2;
                    }
                }
            }
        }
    }
}

function eval_pso(ctx, data)
{
    for (var i=0; i < particles.length; i++)
    {
        with(particles[i])
        {
            var val = peval(ctx, data);
            if (val > pbest_val)
            {
                pbest = pos.slice();
                pbest_val = val;
            }
            if (val > gbest_val)
            {
                gbest = pos.slice();
                gbest_val = val;
                gbest_index = i;
            }
        }
    }
    particles[gbest_index].draw(ctx);
    return gbest_val;
}